import os
import struct
import re
import threading
from typing import Optional, List

class PageManager:
    """
    页式存储管理器
    负责数据页的分配、释放、读写和元数据管理。
    设计决策：
      - 页大小固定为4KB，便于与操作系统页对齐，提升I/O效率。
      - 每个页独立存储为单独文件，便于调试和灵活管理（适合教学/实验型系统）。
      - 采用简单的文件命名规则，便于快速定位和遍历所有页。
      - 统一异常处理，保证健壮性和易于调试。
      - 线程安全：通过锁机制保证多线程环境下的一致性。
    注意事项：
      - 当前实现适合小型/教学系统，大规模生产环境建议采用大文件分区+页表管理。
      - 页ID分配策略简单，后续可扩展为空闲页回收、位图管理等。
      - 支持页压缩、加密、批量I/O、内存映射等高级功能的扩展点。
    """

    PAGE_SIZE = 4096  # 页大小固定为4KB

    def __init__(self, data_dir: str = "data"):
        """
        初始化页管理器

        Args:
            data_dir: 数据文件存储目录
        """
        self.data_dir = data_dir
        os.makedirs(data_dir, exist_ok=True)
        self._lock = threading.RLock()  # 保证多线程环境下的安全

    def _get_page_path(self, page_id: int) -> str:
        """
        获取页面对应的文件路径
        """
        return os.path.join(self.data_dir, f"page_{page_id}.dat")

    def allocate_page(self, page_id: int) -> bool:
        """
        分配一个新页
        若页已存在则返回False，否则创建新页并初始化为0。

        Args:
            page_id: 页编号

        Returns:
            成功返回True，失败返回False（如页已存在）
        """
        with self._lock:
            page_path = self._get_page_path(page_id)
            if os.path.exists(page_path):
                return False  # 页已存在

            # 创建新页并初始化为0
            with open(page_path, 'wb') as f:
                f.write(bytes([0] * self.PAGE_SIZE))
            return True

    def release_page(self, page_id: int) -> bool:
        """
        释放一个页（物理删除页文件）

        Args:
            page_id: 页编号

        Returns:
            成功返回True，失败返回False（如页不存在）
        """
        with self._lock:
            page_path = self._get_page_path(page_id)
            if not os.path.exists(page_path):
                return False  # 页不存在

            os.remove(page_path)
            return True

    def read_page(self, page_id: int) -> Optional[bytes]:
        """
        读取页内容

        Args:
            page_id: 页编号

        Returns:
            页内容（bytes），如果页不存在则返回None
        """
        with self._lock:
            page_path = self._get_page_path(page_id)
            if not os.path.exists(page_path):
                return None

            with open(page_path, 'rb') as f:
                data = f.read()
                # 若实际读取不足PAGE_SIZE，补齐
                if len(data) < self.PAGE_SIZE:
                    data += bytes([0] * (self.PAGE_SIZE - len(data)))
                return data

    def write_page(self, page_id: int, data: bytes) -> bool:
        """
        写入页内容

        Args:
            page_id: 页编号
            data: 要写入的数据，长度必须为PAGE_SIZE

        Returns:
            成功返回True，失败返回False（如长度不符）
        """
        if len(data) != self.PAGE_SIZE:
            return False

        with self._lock:
            page_path = self._get_page_path(page_id)
            with open(page_path, 'wb') as f:
                f.write(data)
            return True

    def page_exists(self, page_id: int) -> bool:
        """
        检查页是否存在

        Args:
            page_id: 页编号

        Returns:
            页存在返回True，否则返回False
        """
        page_path = self._get_page_path(page_id)
        return os.path.exists(page_path)

    def get_all_page_ids(self) -> List[int]:
        """
        获取所有已分配的页面ID
        设计说明：通过文件名模式匹配，快速遍历所有页。
        Returns: 页面ID列表（升序）
        """
        page_ids = []
        if os.path.exists(self.data_dir):
            pattern = re.compile(r'page_(\d+)\.dat')
            for filename in os.listdir(self.data_dir):
                match = pattern.match(filename)
                if match:
                    page_ids.append(int(match.group(1)))
        return sorted(page_ids)
